home *** CD-ROM | disk | FTP | other *** search
/ Delphi Magazine Collection 2001 / Delphi Magazine Collection 20001 (2001).iso / DISKS / Issue35 / hint95 / HINT95.ZIP / HINT95.PAS < prev   
Encoding:
Pascal/Delphi Source File  |  1998-06-04  |  11.3 KB  |  390 lines

  1. unit Hint95;
  2. {
  3.   Hint95 version 1.05 *** BETA ***
  4.  
  5.   by Torsten Detsch
  6.   email: tdetsch@bigfoot.com
  7.  
  8.  
  9.   You are free to use, modify and distribute this code as you like. But I
  10.   ask you to send me a copy of new versions. And please give me credit when
  11.   you use parts of my code in other components or applications.
  12.  
  13.  
  14.   Credits: THint95 is based on TDanHint by Dan Ho (danho@cs.nthu.edu.tw).
  15.   I also got some ideas from TToolbar97 by Jordan Russell (jordanr@iname.com).
  16.  
  17.  
  18.   Changes to this version:
  19.  
  20.   1.05  Fixes and minor improvements:
  21.           - Dropped some source code that was not necessary.
  22.           - Joe Chizmas fixed a bug that caused Delphi 3 to loose its hints when
  23.             used together with Hint95.
  24.           - Changed the code for finding the font Tahoma again. Now there is a
  25.             Boolean variable that holds the state of the font Tahoma. This var
  26.             is updates whenever a WM_FONTCHANGE occurs.
  27.           - Hopefully fixed a bug that caused the tooltips to have a wordbreak
  28.             when there shouldn't be one. 
  29.  
  30. }
  31.  
  32. {$IFNDEF WIN32} Delphi 1 is not supported by Hint95. Sorry! {$ENDIF}
  33.  
  34. interface
  35.  
  36. uses
  37.   Classes, Windows, Graphics, Messages, Controls, Forms;
  38.  
  39. const
  40.   Hint95Version = '1.05';
  41.  
  42. type
  43.   { THint95 }
  44.  
  45.   THintStyle = (hsFlat, hsOffice97, hsWindows95);
  46.  
  47.   THint95 = class(TComponent)
  48.   private
  49.     FTahomaAvail: Boolean; { True when Tahoma is available. }
  50.     FHintFont: TFont;
  51.     FHintStyle: THintStyle;
  52.     FWindowHandle: HWND;
  53.     FOnShowHint: TShowHintEvent;
  54.     procedure GetHintInfo(var HintStr: string; var CanShow: Boolean; var HintInfo: THintInfo);
  55.     procedure GetTahomaAvail;
  56.     procedure GetTooltipFont;
  57.     procedure SetHintStyle(AHintStyle: THintStyle);
  58.     procedure WndProc(var Msg: TMessage);
  59.   protected
  60.   public
  61.     constructor Create(AOwner: TComponent); override;
  62.     destructor Destroy; override;
  63.   published
  64.     property HintStyle: THintStyle read FHintStyle write SetHintStyle default hsWindows95;
  65.     property OnShowHint: TShowHintEvent read FOnShowHint write FOnShowHint;
  66.   end;
  67.  
  68.   { THintWindow95 }
  69.  
  70.   THintWindow95 = class(THintWindow)
  71.   private
  72.     FHint: THint95;
  73.     FTextHeight, FTextWidth: Integer;
  74.     function FindHint95: THint95;
  75.   protected
  76.     procedure Paint; override;
  77.     procedure CreateParams(var Params: TCreateParams); override;
  78.   public
  79.     procedure ActivateHint(Rect: TRect; const AHint: string); Override;
  80.   published
  81.   end;
  82.  
  83. procedure Register;
  84.  
  85. implementation
  86.  
  87. var
  88.   HintControl: TControl; { control the tooltip belongs to }
  89.   HintMaxWidth: Integer; { max width of the tooltip }
  90.  
  91. procedure Register;
  92. begin
  93.   RegisterComponents('Tools', [THint95]);
  94. end;
  95.  
  96. constructor THint95.Create(AOwner: TComponent);
  97. begin
  98.   inherited Create(AOwner);
  99.  
  100.   if not (csDesigning in ComponentState) then begin
  101.     HintWindowClass := THintWindow95;
  102.     FWindowHandle := AllocateHWnd(WndProc);
  103.  
  104.     with Application do begin
  105.       ShowHint := not ShowHint;
  106.       ShowHint := not ShowHint;
  107.       OnShowHint := GetHintInfo;
  108.  
  109.       { NOTE: These values are similar to those Win95 uses. But Win95
  110.         does only display a tooltip when the mouse cursor doesn't move
  111.         on the control anymore. Delphi doesn't do this. }
  112.       HintShortPause := 25;
  113.       HintPause := 500;
  114.       HintHidePause := 5000;
  115.     end;
  116.   end;
  117.  
  118.   FHintStyle := hsWindows95;
  119.   FHintFont := TFont.Create;
  120.   FHintFont.Color := clInfoText;
  121.  
  122.   GetTahomaAvail;
  123.   GetTooltipFont;
  124. end;
  125.  
  126. destructor THint95.Destroy;
  127. begin
  128.   FHintFont.Free;
  129.   if not (csDesigning in ComponentState) then DeallocateHWnd(FWindowHandle);
  130.   inherited Destroy;
  131. end;
  132.  
  133. procedure THint95.GetHintInfo(var HintStr: string; var CanShow: Boolean; var HintInfo: THintInfo);
  134. begin
  135.   if Assigned(FOnShowHint) then FOnShowHint(HintStr, CanShow, HintInfo);
  136.   HintControl := HintInfo.HintControl;
  137.   HintMaxWidth := HintInfo.HintMaxWidth;
  138. end;
  139.  
  140. procedure THint95.GetTahomaAvail;
  141. begin
  142.   FTahomaAvail := Screen.Fonts.IndexOf('Tahoma') <> -1;
  143. end;
  144.  
  145. procedure THint95.GetTooltipFont;
  146. var
  147.   NCM: TNonClientMetrics;
  148. begin
  149.   { Get tooltip font using SystemParametersInfo }
  150.   NCM.cbSize := SizeOf(TNonClientMetrics);
  151.   SystemParametersInfo(SPI_GETNONCLIENTMETRICS, NCM.cbSize, @NCM, 0);
  152.   with NCM.lfStatusFont, FHintFont do begin
  153.     Name := lfFaceName;
  154.     Height := lfHeight;
  155.     Style := [];
  156.     if lfWeight > FW_MEDIUM then Style := Style + [fsBold];
  157.     if lfItalic <> 0 then Style := Style + [fsItalic];
  158.     if lfUnderline <> 0 then Style := Style + [fsUnderline];
  159.     if lfStrikeOut <> 0 then Style := Style + [fsStrikeOut];
  160.     Pitch := TFontPitch(lfPitchAndFamily);
  161.     {$IFNDEF VER90} { Delphi 3 or C++Builder }
  162.     CharSet := TFontCharSet(lfCharSet);
  163.     {$ENDIF}
  164.   end;
  165.  
  166.   { Office 97 style? Then use Tahoma instead of MS Sans Serif }
  167.   if (FHintStyle=hsOffice97) and (FHintFont.Name='MS Sans Serif') and FTahomaAvail then
  168.     FHintFont.Name := 'Tahoma';
  169. end;
  170.  
  171. procedure THint95.SetHintStyle(AHintStyle: THintStyle);
  172. begin
  173.   if AHintStyle <> FHintStyle then begin
  174.     FHintStyle := AHintStyle;
  175.     if FHintStyle = hsOffice97 then GetTooltipFont;
  176.   end;
  177. end;
  178.  
  179. procedure THint95.WndProc(var Msg: TMessage);
  180. begin
  181.   with Msg do
  182.     case Msg of
  183.       WM_SETTINGCHANGE: GetTooltipFont;
  184.       WM_FONTCHANGE: GetTahomaAvail;
  185.       { ^ Update TahomaAvail whenever a font was installed or removed. }
  186.       else Result := DefWindowProc(FWindowHandle, Msg, wParam, lParam);
  187.     end;
  188. end;
  189.  
  190. { THintWindow95 }
  191.  
  192. function THintWindow95.FindHint95: THint95;
  193. var
  194.   I: Integer;
  195. begin
  196.   Result := nil;
  197.  
  198.   with Application.MainForm do
  199.   for I := 0 to ComponentCount-1 do
  200.     if Components[I] is THint95 then begin
  201.       Result := THint95(Components[I]);
  202.       Break;
  203.     end;
  204. end;
  205.  
  206. procedure THintWindow95.CreateParams(var Params: TCreateParams);
  207. begin
  208.   inherited CreateParams(Params);
  209.   Params.Style := Params.Style - WS_BORDER;
  210. end;
  211.  
  212. procedure THintWindow95.Paint;
  213. var
  214.   DC: HDC;
  215.   R, RD: TRect;
  216.   Brush, SaveBrush: HBRUSH;
  217.  
  218.   { DCFrame3D was taken from TToolbar97 by Jordan Russell }
  219.   procedure DCFrame3D(var R: TRect; const TopLeftColor, BottomRightColor: TColor);
  220.   { Similar to VCL's Frame3D function, but accepts a DC rather than a Canvas }
  221.   var
  222.     Pen, SavePen: HPEN;
  223.     P: array[0..2] of TPoint;
  224.   begin
  225.     Pen := CreatePen(PS_SOLID, 1, ColorToRGB(TopLeftColor));
  226.     SavePen := SelectObject(DC, Pen);
  227.     P[0] := Point(R.Left, R.Bottom-2);
  228.     P[1] := Point(R.Left, R.Top);
  229.     P[2] := Point(R.Right-1, R.Top);
  230.     PolyLine(DC, P, 3);
  231.     SelectObject(DC, SavePen);
  232.     DeleteObject(Pen);
  233.  
  234.     Pen := CreatePen(PS_SOLID, 1, ColorToRGB(BottomRightColor));
  235.     SavePen := SelectObject(DC, Pen);
  236.     P[0] := Point(R.Left, R.Bottom-1);
  237.     P[1] := Point(R.Right-1, R.Bottom-1);
  238.     P[2] := Point(R.Right-1, R.Top-1);
  239.     PolyLine(DC, P, 3);
  240.     SelectObject(DC, SavePen);
  241.     DeleteObject(Pen);
  242.   end;
  243.  
  244. begin
  245.   DC := Canvas.Handle;
  246.   R := ClientRect; RD := ClientRect;
  247.  
  248.   { Background }
  249.   Brush := CreateSolidBrush(GetSysColor(COLOR_INFOBK));
  250.   SaveBrush := SelectObject(DC, Brush);
  251.   FillRect(DC, R, Brush);
  252.   SelectObject(DC, SaveBrush);
  253.   DeleteObject(Brush);
  254.  
  255.   { Border }
  256.   case FHint.FHintStyle of
  257.     hsFlat: DCFrame3D(R, clWindowFrame, clWindowFrame);
  258.     else    DCFrame3D(R, cl3DLight, cl3DDkShadow);
  259.   end;
  260.  
  261.   { Caption }
  262.   SetBkMode(DC, TRANSPARENT);
  263.   RD.Left := R.Left + (R.Right-R.Left - FTextWidth) div 2;
  264.   RD.Top := R.Top + (R.Bottom-R.Top - FTextHeight) div 2;
  265.   RD.Bottom := RD.Top + FTextHeight;
  266.   DrawText(DC, @Caption[1], Length(Caption), RD, DT_NOCLIP or DT_NOPREFIX or DT_WORDBREAK);
  267. end;
  268.  
  269. procedure THintWindow95.ActivateHint(Rect: TRect; const AHint: string);
  270. var
  271.   dx, dy, rch: Integer;
  272.   Pnt, P: TPoint;
  273.   II: TIconInfo;
  274.  
  275.   function RealCursorHeight(Cur: HBITMAP): Integer;
  276.   { Scans a cursor bitmap to get its real height }
  277.   var
  278.     Bmp: TBitmap;
  279.     x, y: Integer;
  280.     found: Boolean;
  281.   begin
  282.     Result := 0;
  283.  
  284.     Bmp := TBitmap.Create;
  285.     Bmp.Handle := Cur;
  286.  
  287.     { Scan the "normal" cursor mask (lines 1 to 32) }
  288.     for y := 31 downto 0 do begin
  289.       for x := 0 to 31 do begin
  290.         found := GetPixel(Bmp.Canvas.Handle, x, y)=clBlack;
  291.         if found then Break;
  292.       end;
  293.  
  294.       if found then begin
  295.         Result := y-II.yHotSpot;
  296.         Break;
  297.       end;
  298.     end;
  299.  
  300.     { No result? Then scan the inverted mask (lines 32 to 64) }
  301.     if not found then
  302.     for y := 63 downto 31 do begin
  303.       for x := 0 to 31 do begin
  304.         found := GetPixel(Bmp.Canvas.Handle, x, y)=clWhite;
  305.         if found then Break;
  306.       end;
  307.  
  308.       if found then begin
  309.         Result := y-II.yHotSpot-32;
  310.         Break;
  311.       end;
  312.     end;
  313.  
  314.     { No result yet?! Ok, let's say the cursor height is 32 pixels... }
  315.     if not found then Result := 32;
  316.  
  317.     Bmp.Free;
  318.   end;
  319.  
  320. begin
  321.   Caption := AHint;
  322.   FHint := FindHint95;
  323.  
  324.   case FHint.FHintStyle of
  325.     hsFlat:               { Internet Explorer style }
  326.       begin dx := 6; dy := 4; end;
  327.     hsOffice97:           { Office 97 style }
  328.       begin dx := 6; dy := 6; end;
  329.     hsWindows95:          { Windows 95 style }
  330.       begin dx := 8; dy := 4; end;
  331.   end;
  332.  
  333.   Canvas.Font.Assign(FHint.FHintFont);
  334.   with Rect do begin
  335.     { Calculate width and height }
  336.     Rect.Right := Rect.Left + HintMaxWidth - dx; { this hopefully fixes the problem with HintMaxWidth }
  337.     DrawText(Canvas.Handle, @AHint[1], Length(AHint), Rect, DT_CALCRECT or DT_WORDBREAK or DT_NOPREFIX);
  338.     Inc(Right, dx); Inc(Bottom, dy);
  339.     FTextWidth := Right-Left-dx;
  340.     FTextHeight := Bottom-Top-dy;
  341.  
  342.     { Calculate position }
  343.     GetCursorPos(Pnt); GetIconInfo(GetCursor, II);
  344.     Right := Right-Left + Pnt.X; Left := Pnt.X;
  345.     rch := RealCursorHeight(II.hbmMask);
  346.     Bottom := Bottom-Top + Pnt.Y + rch; Top := Pnt.Y + rch;
  347.  
  348.     { Make sure the tooltip is completely visible }
  349.     if Right > Screen.Width then begin
  350.       Left := Screen.Width - Right+Left;
  351.       Right := Left + FTextWidth + dx;
  352.     end;
  353.     if Bottom > Screen.Height then begin
  354.       if (FHint.FHintStyle=hsOffice97) or (HintControl is TForm) then begin
  355.         { Office 97 displays the tooltips 2 pixels above
  356.           the cursor position.
  357.  
  358.           NOTE: Tooltips for forms are included here for 2 reasons:
  359.           1. For forms "HintControl.Parent.ClientToScreen()" causes
  360.              an exception.
  361.           2. Forms are normally very big (at least bigger than buttons)
  362.              and I don't think it looks good when the mouse cursor is
  363.              at the bottom of the screen and the tooltip is at the top. }
  364.         Bottom := Pnt.Y - 2;
  365.         Top := Bottom - FTextHeight - dy;
  366.       end
  367.       else begin
  368.         { Win95 and IE display the tooltips right above the
  369.           control they belong to. }
  370.         if HintControl <> nil then begin
  371.           P := HintControl.Parent.ClientToScreen(Point(0, HintControl.Top));
  372.           Bottom := P.Y;
  373.           Top := Bottom - FTextHeight - dy;
  374.         end;
  375.       end;
  376.     end;
  377.   end;
  378.   BoundsRect := Rect;
  379.  
  380.   Pnt := ClientToScreen(Point(0, 0));
  381.   SetWindowPos(Handle, HWND_TOPMOST, Pnt.X, Pnt.Y, 0, 0, SWP_SHOWWINDOW or SWP_NOACTIVATE or SWP_NOSIZE);
  382. end;
  383.  
  384. const
  385.   Sig: PChar = '- Hint95 version ' + Hint95Version + ' by Torsten Detsch -';
  386.  
  387. initialization
  388.   Sig := Sig;
  389. end.
  390.